﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace game
{
    public class Fighter : Ship
    {
        protected ShipEnginePSystem pSystem;
        protected ShipEnginePSystem pSystem2;
        protected ShipTraceHandler traceHandler;

        protected Vector3 pSystemOffset;
        protected Vector3 pSystem2Offset;

        

        protected float moveAcc = 0.1f;
        protected float maxMoveSpeed = 50.0f;
        protected float maxLeanZ = (float)Math.PI / 3.5f; // in radians

        protected float leanSpeed = 0.003f;
        protected float leanEpsilon = 0.1f;

        protected bool dodging = false;

        protected bool flyingDown = false;
        protected bool flyingUp = false;
        protected bool isLayerToChange = false;
        protected float flyDownAcc = 25.0f;

        protected bool leanOrdered = false;
        protected bool speedUpOrdered = false;

        protected RegularMovement regMove = null;
        protected FlyDownMovement2 flyDownMove = null;
        protected FlyUpMovement flyUpMove = null;
        protected RollDogdeMovement dodgeMove = null;

        public Fighter(Scene scene, ModelUnit model, int layerIndex, Vector3 position, Squadron squadron)
            : base(scene, model, layerIndex, position, squadron)
        {
            this.rotation.Y = 0.0f;
            this.rotation.Z = 0.0f;
            this.moveSpeed = 0.0f;
            this.moveDir = Vector3.Transform(Vector3.Forward, Matrix.CreateRotationY(rotation.Y)); ;
            this.World = Matrix.CreateRotationY(Rotation.Y) * Matrix.CreateTranslation(position);

            Weapons.Add(new PlasmaGun(scene, this));

            if(squadron.Army.ID == 1)
                Weapons.Add(new Blaster(scene, this, BlasterCell.Color.RED));
            else
                Weapons.Add(new Blaster(scene, this, BlasterCell.Color.GREEN));

            RocketLauncher = new RocketLauncher(scene, this);
            //Weapons.Add(new ThunderboltGun(scene, this));

            ActiveWeaponIndex = 0;

            this.Health = 50.0f;
            this.MaxHealth = this.Health;
            this.damage = 80.0f;

            PPEffectTypes = new List<PostProcessEffectType>();
            PPEffectTypes.Add(PostProcessEffectType.GLOW);

            regMove = new RegularMovement();
            regMove.LeanStablePointEpsilon = leanEpsilon;
            regMove.LeanSpeed = leanSpeed;
            regMove.MaxLean = maxLeanZ;
            regMove.MaxMoveSpeed = maxMoveSpeed;
            regMove.MoveAcc = moveAcc;

            flyDownMove = new FlyDownMovement2();
            flyUpMove = new FlyUpMovement();

            dodgeMove = new RollDogdeMovement();

            pSystem = new ShipEnginePSystem(scene, position);
            pSystem.Initialize();
            pSystemOffset = new Vector3(80.0f, -200.0f, -1100.0f);

            pSystem2 = new ShipEnginePSystem(scene, position);
            pSystem2.Initialize();
            pSystem2Offset = new Vector3(-80.0f, -200.0f, -1100.0f);

            traceHandler = new ShipTraceHandler(scene, position, 4);

            Shield = new Shield(scene, this, 23.0f, new Color(255,255,255),500);
        }

        /// <summary>
        /// shall be called with base.Update() at the end of derived class Update()
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            for (int i = 0; i < Weapons.Count; i++)
            {
                Weapons[i].Update(gameTime);
            }
            RocketLauncher.Update(gameTime);

            if (isLayerToChange)
            {
                if (flyingDown && (Math.Abs(position.Y - flyDownMove.NewLayerHeight)
                    < Math.Abs(flyDownMove.NewLayerHeight - flyDownMove.OldLayerHeight) * 0.5f))
                {
                    isLayerToChange = false;
                    scene.RemoveObject(this);
                    LayerIndex--;
                    scene.AddObject(this, LayerIndex);
                }
                else if (flyingUp && (Math.Abs(position.Y - flyUpMove.NewLayerHeight)
                    < Math.Abs(flyUpMove.NewLayerHeight - flyUpMove.OldLayerHeight) * 0.5f))
                {
                    isLayerToChange = false;
                    scene.RemoveObject(this);
                    LayerIndex++;
                    scene.AddObject(this, LayerIndex);
                }
            }

            if (flyingDown)
            {
                flyingDown = !flyDownMove.Finished;
                flyDownMove.RotateZAndFall(ref rotation.Z, ref position);
            }
            else if (flyingUp)
            {
                flyingUp = !flyUpMove.Finished;
                flyUpMove.RotateXAndHover(ref rotation.X, ref position);
            }
            else if (dodging)
            {
                dodging = !dodgeMove.Finished;
                rotation.Z = dodgeMove.RotateZ(rotation.Z);
                position = dodgeMove.Dodge(position, rotation.Y);
            }
            else
            {
                if (rotation.Z != 0.0f && !leanOrdered)
                {
                    rotation.Z = regMove.Lean(rotation.Z, RegularMovement.LeanState.IDLE);
                }

                rotation.Y = regMove.Rotate(rotation.Y, rotation.Z * 0.003f);
            }

            Matrix rot = Matrix.CreateRotationZ(rotation.Z) * Matrix.CreateRotationX(rotation.X)
                * Matrix.CreateRotationY(rotation.Y);
            //Vector3 movement = Vector3.Transform(Vector3.Forward, rot);
            regMove.MoveWithVibration(ref position, moveSpeed, ref moveDir, out position, out velocity);
            World = rot * Matrix.CreateTranslation(position);
            bSphereTransformedUpToDate = false;
            

            Vector3 pSystemOffsetRotated;
            Vector3.Transform(ref pSystemOffset, ref rot, out pSystemOffsetRotated);
            pSystem.Update(gameTime, position - pSystemOffsetRotated, rot, velocity,
                speedUpOrdered && (moveSpeed < maxMoveSpeed || leanOrdered));
            Vector3.Transform(ref pSystem2Offset, ref rot, out pSystemOffsetRotated);
            pSystem2.Update(gameTime, position - pSystemOffsetRotated, rot, velocity,
                speedUpOrdered && (moveSpeed < maxMoveSpeed || leanOrdered));

            traceHandler.Update(gameTime, position, rot, velocity);

            leanOrdered = false;
            speedUpOrdered = false;

            timeFromLastCycleMs += gameTime.ElapsedGameTime.Milliseconds;

            Shield.Update(gameTime);
        }


        public override void DrawTransparent(GameTime gameTime)
        {
            if (pSystem != null)
            {
                pSystem.Draw(gameTime);
                pSystem2.Draw(gameTime);
            }

            traceHandler.Draw(gameTime);

            if (Shield != null)
                Shield.Draw(gameTime);
        }

        protected override void Destroyed()
        {
            base.Destroyed();
            //scene.ExplosionMgr.GreenExplosion(position, 10000, 200);
            scene.ExplosionMgr.BigRedExplosion(position);
            scene.ShockWave(position, 1.0f);
            scene.SoundMgr.Ship(position);
        }

        #region commands

        protected bool TurnOnShield()
        {
            return Shield.TurnOn();
        }

        protected bool ShootWeapon()
        {
            Weapon weapon = Weapons[ActiveWeaponIndex];
            if (weapon.CanShoot())
            {
                return weapon.Shoot(LayerIndex, position, rotation, moveDir * moveSpeed);
            }
            return false;
        }

        protected bool ShootRocket()
        {
            if (RocketLauncher.CanShoot())
            {
                return RocketLauncher.Shoot(LayerIndex, position, rotation, moveDir * moveSpeed);
            }
            return false;
        }

        protected void SpeedUp()
        {
            Vector3 orientation = Vector3.Transform(Vector3.Forward, Matrix.CreateRotationY(rotation.Y));
            regMove.EngineOn(ref moveSpeed, ref moveDir, orientation, moveAcc,
                out moveSpeed, out moveDir);
            speedUpOrdered = true;
            //MoveSpeed = regMove.ChangeSpeed(MoveSpeed, moveAcc, maxMoveSpeed, true);
        }

        protected void SlowDown()
        {
            //MoveSpeed = regMove.ChangeSpeed(MoveSpeed, moveAcc, maxMoveSpeed, false);
        }

        protected void LeanLeft()
        {
            if (!flyingDown && !flyingUp && !dodging)
            {
                rotation.Z = regMove.Lean(rotation.Z, RegularMovement.LeanState.LEFT);
                leanOrdered = true;
            }
        }

        protected void LeanRight()
        {
            if (!flyingDown && !flyingUp && !dodging)
            {
                rotation.Z = regMove.Lean(rotation.Z, RegularMovement.LeanState.RIGHT);
                leanOrdered = true;
            }
        }

        /// <summary>
        /// </summary>
        /// <returns>False, when unable to change layer</returns>
        protected bool FlyDown()
        {
            if (LayerIndex > scene.GetBottomLayerIndex() && !flyingUp && !flyingDown)
            {
                flyingDown = true;
                isLayerToChange = true;
                flyDownMove.Start(position.Y, scene.GetLayerHeight(LayerIndex - 1));
                return true;
            }
            else
            {
                return false;
            }
        }

        protected bool FlyUp()
        {
            if (LayerIndex < scene.GetTopLayerIndex() && !flyingUp && !flyingDown)
            {
                flyingUp = true;
                isLayerToChange = true;
                flyUpMove.Start(position.Y, scene.GetLayerHeight(LayerIndex + 1));
                return true;
            }
            else
            {
                return false;
            }
        }

        protected void DodgeLeft()
        {
            if (!flyingDown && !dodging)
            {
                dodgeMove.Start(RollDogdeMovement.DodgeDir.LEFT);
                dodging = true;
            }
        }

        protected void DodgeRight()
        {
            if (!flyingDown && !dodging)
            {
                dodgeMove.Start(RollDogdeMovement.DodgeDir.RIGHT);
                dodging = true;
            }
        }

        #endregion

        public override void Collision(GameObject obj)
        {
            base.Collision(obj);
            scene.SoundMgr.Hit(position);
        }

    }
}
